
#ifndef RTCONNECTORUDPT_H
#define RTCONNECTORUDPT_H

#include "RtReactorInterface.h"
#include "RtReferenceControl.h"
#include "RtInetAddr.h"
#include "RtObserver.h"
#include "RtDnsManager.h"
#include "RtUdpEndpoint.h"

template <class UpperType, class UpTrptType, class UpSockType>
class /*RT_API_EXPORT*/ CRtConnectorUdpT 
	: public IRtConnectorInternal
	, public IRtTimerHandler
	, public IRtObserver 
{
public:
	CRtConnectorUdpT(IRtReactor *aReactor, UpperType &aUpper)
		: m_pReactor(aReactor)
		, m_Upper(aUpper)
		, m_pTransport(NULL)
		, m_bResolving(FALSE)
	{
		m_pEndpointManager = CRtUdpEndpointManager::Instance();
	}
	
	virtual ~CRtConnectorUdpT()
	{
		Close();
	}

	// interface IRtConnectorInternal
	virtual int Connect(const CRtInetAddr &aAddr, CRtInetAddr *aAddrLocal = NULL)
	{
		const CRtInetAddr *pAddrConnect = &aAddr;
		if (aAddrLocal && aAddrLocal != &m_addrLocal)
			m_addrLocal = *aAddrLocal;

#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
		if (!aAddr.IsResolved()) {
			m_addrUnResolved = aAddr;
			pAddrConnect = &m_addrUnResolved;

			CRtAutoPtr<CRtDnsRecord> pRecord;
			CRtString strHostName = m_addrUnResolved.GetHostName();
			RtResult rv = CRtDnsManager::Instance()->AsyncResolve(
				pRecord.ParaOut(),
				strHostName,
				this);
			if (RT_SUCCEEDED(rv)) {
				DWORD dwIp = *(pRecord->begin());
				rv = m_addrUnResolved.SetIpAddrBy4Bytes(dwIp);
				if (RT_FAILED(rv)) {
					RT_ERROR_TRACE_THIS("CRtConnectorUdpT::Connect, wrong ip addr from DNS,"
						" dwIp=" << dwIp << " hostname=" << strHostName);
					return -1;
				}
				if (m_bResolving) {
					CRtDnsManager::Instance()->CancelResolve(this);
					m_bResolving = FALSE;
				}
				RT_INFO_TRACE_THIS("CRtConnectorUdpT::Connect, resolved ip="<<m_addrUnResolved.GetIpDisplayName());
			}
			else if (rv == RT_ERROR_WOULD_BLOCK) {
				m_bResolving = TRUE;
				return 0;
			}
			else
				return -1;
		}
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME

		RtResult rv = m_pEndpointManager->Connect(*pAddrConnect, aAddrLocal, m_pTransport);
		if (RT_FAILED(rv))
			return -1;

		// can't use NotifyHandler(WRITE_MASK) due to not RegiesterHandler.
#ifdef RT_DEBUG
		rv = 
#endif // RT_DEBUG
			m_pReactor->ScheduleTimer(this, NULL, CRtTimeValue::s_tvZero, 1);
		RT_ASSERTE(RT_SUCCEEDED(rv));
		return 0;
	}

	virtual int Close()
	{
		if (m_pReactor)
			m_pReactor->CancelTimer(this);

		m_pTransport = NULL;

#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
		if (m_bResolving) {
			CRtDnsManager::Instance()->CancelResolve(this);
			m_bResolving = FALSE;
		}
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
		return 0;
	}

	void OnTimeout(const CRtTimeValue &aCurTime, LPVOID aArg)
	{
		UpTrptType* pTrans = m_pTransport;
		m_pTransport = NULL;
		m_Upper.OnConnectIndication(RT_OK, pTrans, this);
	}

	void OnObserve(LPCSTR aTopic, LPVOID aData)
	{
		RT_ASSERTE(CRtThreadManager::IsEqualCurrentThread(CRtThreadManager::TT_NETWORK));
		RT_ASSERTE(!strcmp(aTopic, "DnsManager"));

		int nErr = *static_cast<int*>(aData);
		if (nErr || Connect(m_addrUnResolved, &m_addrLocal) == -1) {
			Close();
			m_Upper.OnConnectIndication(
				RT_ERROR_NETWORK_DNS_FAILURE,
				NULL, 
				this);
		}
	}
	
private:
	IRtReactor *m_pReactor;
	UpperType &m_Upper;
	UpTrptType *m_pTransport;
	CRtInetAddr m_addrUnResolved;
	CRtInetAddr m_addrLocal;
	BOOL m_bResolving;
	CRtUdpEndpointManager* m_pEndpointManager;
};

#endif // !RTCONNECTORUDPT_H
